from pygame.locals import *
import pygame

from pgEngine.point import Point, AxisAlignedBoundingBox

from colors import *


# TODO: Move the selection stuff here somewhere.


class EditPalette:
    def __init__(self, window, editMode):
        self.edit = editMode
        self.window = window
        self.map = window.map
        self.screen = self.window.screen

    def drawMenu(self):
        # Draw the background and toolbox.
        self.edit.toolBox.drawMenu()
        
    
class TilePalette(EditPalette):
    tilePage = 0
    tileNames = None
    
    tileRow = 0
    tileCol = 0
    page = None
    tileName = ""
    tiles = None
    
    tileRows = 0
    tileCols = 4
    tilePages = 0
    
    menu = None
    rcMenu = None
    rcMenuItems = None
    
    
    def __init__(self, window, editMode):
        EditPalette.__init__(self, window, editMode)

        self.rcMenu = window.overlayRects["menu"]
        self.tileCols = 4
        self.tilePages = 11
        self.selectPage(0)
        self.selectTile(name="water2")

    def selectPageAndTile(self, name):
        self.selectPage(self.map.tiles[name][2])
        self.selectTile(name=name)

    def selectPage(self, page):
        self.tilePage = page
        self.tileNames = []
        for tileName in self.map.tiles.keys():
            if self.map.tiles[tileName][2] == self.tilePage:
                self.tileNames.append(tileName)
        self.tileNames.sort()
        
        # Make sure we (re)build the menu.
        self.createMenu()

    def selectTile(self, num=None, row=None, col=None, name=None):
        if name:
            tileNum = self.tileNames.index(name)
            self.tileRow = tileNum / self.tileCols
            self.tileCol = tileNum % self.tileCols
            self.tileName = self.tileNames[tileNum]
        elif num is not None:
            if num >= len(self.tileNames):
                return self.selectTile(num=len(self.tileNames)-1)
            elif num < 0:
                num += len(self.tileNames)
            self.tileRow = num / self.tileCols
            self.tileCol = num % self.tileCols
            self.tileName = self.tileNames[num]
        else:
            tileNum = row * self.tileCols + col
            if tileNum >= len(self.tileNames):
                return self.selectTile(num=len(self.tileNames)-1)
            self.tileRow = row
            self.tileCol = col
            self.tileName = self.tileNames[tileNum]

    def createMenu(self):
        self.rcMenuItems = pygame.Rect(self.rcMenu.left + 24,
                                       self.rcMenu.top + 40,
                                       self.map.tileSize.x * 2,
                                       ((self.map.tileSize.y) *
                                        len(self.tileNames)) / 8)

        self.menu = pygame.Surface(self.rcMenuItems.size)
        self.menu = self.menu.convert()

        self.tiles = {}
        row = -1
        for n, name in enumerate(self.tileNames):
            if name not in self.tiles:
                tile = self.map.mapTiles[name][0]
                scaledTile = pygame.transform.rotozoom(tile, 0, 0.5)
                self.tiles[name] = scaledTile
                
            col = n % self.tileCols
            if col == 0:
                row += 1
            self.menu.blit(self.tiles[name],
                           (col*self.map.tileSize.x/2,
                            row*self.map.tileSize.y/2))
        self.tileRows = row + 1
        
        self.page = self.window.font.render("page %i" % (self.tilePage+1),
                                            1, (220,220,220))

    
    def drawMenu(self):
        EditPalette.drawMenu(self)
        self.screen.blit(self.page, (self.rcMenuItems.centerx -
                                            self.page.get_width()/2,
                                     self.rcMenuItems.bottom + 4))
        
        self.screen.blit(self.menu, self.rcMenuItems.topleft)
    
        rcItem = pygame.Rect(self.rcMenuItems.left + self.tileCol *
                                    self.map.tileSize.x/2,
                             self.rcMenuItems.top + self.tileRow *
                                    self.map.tileSize.y/2,
                             self.map.tileSize.x/2,
                             self.map.tileSize.y/2)
        pygame.draw.rect(self.screen, white, rcItem, 2)
        pygame.draw.rect(self.screen, black, rcItem, 1)
        
        mousePos = (self.window.mouseX, self.window.mouseY)
        # Do the hover popup
        if self.rcMenuItems.collidepoint(mousePos):
            posX = mousePos[0] - self.rcMenuItems.left
            posY = mousePos[1] - self.rcMenuItems.top
            col = 2 * posX / self.map.tileSize.x
            row = 2 * posY / self.map.tileSize.y
            if row * 4 + col < len(self.tileNames):
                tileName = self.tileNames[row * 4 + col]
                srfTileName = self.window.font.render(tileName,
                                                      True,
                                                      (255,255,255))
                self.screen.blit(srfTileName,
                                 (self.rcMenuItems.centerx -
                                        srfTileName.get_width() / 2,
                                  self.rcMenuItems.top -
                                        srfTileName.get_height() - 6))
                drawPos = (col*self.map.tileSize.x/2 +
                               self.rcMenuItems.left -
                               self.map.tileSize.x/4,
                           row*self.map.tileSize.y/2 +
                               self.rcMenuItems.top -
                               self.map.tileSize.y/4)
                rcOutline = pygame.Rect((drawPos[0] - 2,
                                         drawPos[1] - 2),
                                        (self.map.tileSize.x + 4,
                                         self.map.tileSize.y + 4))
                pygame.draw.rect(self.screen, white, rcOutline)
                pygame.draw.rect(self.screen, black, rcOutline, 1)
                self.screen.blit(self.map.mapTiles[tileName][0], drawPos)
        return False
    
    def onMouseDown(self, event):
        retVal = None
        
        if event.button == 1:
            # Select a new tile
            posX = event.pos[0] - self.rcMenuItems.left
            posY = event.pos[1] - self.rcMenuItems.top
            col = 2 * posX / self.map.tileSize.x
            row = 2 * posY / self.map.tileSize.y
            self.selectTile(row=row, col=col)
            retVal = False

        elif event.button == 4:
            # Move up a line
            row = (self.tileRow + self.tileRows - 1) % self.tileRows
            if row * self.tileCols + self.tileCol >= len(self.tileNames):
                col = len(self.tileNames) - row * self.tileCols - 1
            else:
                col = self.tileCol
            self.selectTile(row=row,col=col)
            retVal = False
        
        elif event.button == 5:
            # Move down a line
            row = (self.tileRow + 1) % self.tileRows
            if row * self.tileCols + self.tileCol >= len(self.tileNames):
                col = len(self.tileNames) - row * self.tileCols - 1
            else:
                col = self.tileCol
            self.selectTile(row=row,col=col)
            retVal = False
            
        elif event.button == 6:
            # Move to next page
            page = (self.tilePage + 1) % self.tilePages
            self.selectPage(page)
            self.selectTile(num=0)
            retVal = False
            
        elif event.button == 7:
            # Move to previous page
            page = (self.tilePage + self.tilePages - 1) % self.tilePages
            self.selectPage(page)
            self.selectTile(num=0)
            retVal = False
        
        return retVal
    
    def onKeyDown(self, event):
        retVal = None
        
        if event.key == K_TAB:
            if event.mod & KMOD_CTRL == 0:
                # TODO: Work on this for other tile types.
                # Cycle through the types of the current cell
                tileOrder = [ "water", "water2", "water3" ]
                if self.tileName in tileOrder:
                    nextTile = tileOrder.index(self.tileName) + 1
                    self.tileName = tileOrder[nextTile % len(tileOrder)]
                retVal = False
            elif event.mod & KMOD_CTRL:
                # Cycle through the pages of tiles
                retVal = False
                
        elif event.key == K_LEFT:
            if event.mod & KMOD_CTRL == 0:
                # Move to previous tile in list on current page
                # wrapping as necessary
                col = (self.tileCol + 3) % 4
                row = self.tileRow
                if col == 3:
                    row = (self.tileRow + self.tileRows - 1)  % self.tileRows
                self.selectTile(row=row, col=col)
                retVal = False
            else:
                # Move to the previous page in the list
                page = (self.tilePage + (self.tilePages - 1)) % self.tilePages
                self.selectPage(page)
                self.selectTile(num=0)
                retVal = False
            
        elif event.key == K_RIGHT:
            if event.mod & KMOD_CTRL == 0:
                # Move to the next tile in the ist
                col = (self.tileCol + 1) % self.tileCols
                row = self.tileRow
                if col == 0:
                    row = (self.tileRow + 1)  % self.tileRows
                    
                if row * self.tileCols + col >= len(self.tileNames):
                    row = col = 0
                self.selectTile(row=row, col=col)
                retVal = False
            else:
                # Move to the next page in the list
                curPage = (self.tilePage + 1) % self.tilePages
                self.selectPage(curPage)
                self.selectTile(num=0)
                retVal = False
            
        elif event.key == K_DOWN and event.mod & KMOD_CTRL == 0:
            # Move to the next tile in the ist
            col = self.tileCol
            row = (self.tileRow + 1) % self.tileRows
            if row * self.tileCols + col >= len(self.tileNames):
                row = 0
                if col >= len(self.tileNames):
                    col = 0
            self.selectTile(row=row, col=col)
            retVal = False
            
        elif event.key == K_UP and event.mod & KMOD_CTRL == 0:
            # Move to the next tile in the ist
            col = self.tileCol
            row = (self.tileRow + self.tileRows - 1) % self.tileRows
            if row * self.tileCols + col >= len(self.tileNames):
                col = 0
            self.selectTile(row=row, col=col)
            retVal = False
            
        return retVal
        

    def onEvent(self, event):
        retVal = None
        if event.type == MOUSEBUTTONDOWN:
            
            if self.rcMenuItems.collidepoint(event.pos):
                retVal = self.onMouseDown(event)
                
        elif event.type == KEYDOWN:
            retVal = self.onKeyDown(event)
            
        return retVal




class ClipboardItem:
    def __init__(self, tiles, size, wndMap):
        self.tiles = tiles
        self.size = size
        self.map = wndMap
        self.fullsize = None
        self.preview = None
    
    def getFullSize(self):
        if self.fullsize is None:
            mapTiles = self.map.mapTiles
            # Draw it into the preview buffer
            szSurface = (self.size + Point(1, 1)) * self.map.tileSize
            surface = pygame.Surface(szSurface.pos)
            tiles = self.tiles
            y = self.size.y
            x = self.size.x
            for row in range(y, -1, -1):
                for col in range(0, x + 1):
                    tile = tiles[(col, row)]
                    # Draw the tile.
                    scrPos = (Point(col,y - row) * self.map.tileSize)
                    surface.blit(mapTiles[tile][0], scrPos.pos)
            self.fullsize = surface
        
        return self.fullsize
        
    def getPreview(self):
        if self.preview is not None:
            return self.preview
        
        fullSize = self.getFullSize()
        picSize = (self.map.mainWindow.menuW - 40)
        zoom =  picSize / float(fullSize.get_width())
        zoom2 = picSize / float(fullSize.get_height())
        zoom = min(zoom, zoom2)
        self.preview = pygame.transform.rotozoom(fullSize, 0, zoom)
        return self.preview



class ClipboardPalette(EditPalette):
    
    def __init__(self, window, editMode):
        EditPalette.__init__(self, window, editMode)
        self.selection = editMode.selection
        # TODO: Max Clipboard Length
        self.clips = []
        self.curClip = -1
        self.tiles = self.window.game.ocean.tiles
        self.world = self.window.game.ocean
        self.selectionId = None
    
    def cut(self):
        self.curClip = len(self.clips)
        box = AxisAlignedBoundingBox(*self.selection[2:4])
        tiles = dict([((x-box.min.x, y-box.min.y),
                       self.tiles.get((x,y),"water"))
                      for (x,y) in box.enumerate()])
        item = ClipboardItem(tiles, box.size, self.map)
        self.clips.append(item)

        for (x,y) in box.enumerate():
            if (x,y) in self.tiles:
                del self.tiles[(x,y)]

        self.selectionId = self.edit.selectionId

        self.window.map.dirty = True
        self.window.dialogMessage("Cut selection.")
    
    def copy(self):
        self.curClip = len(self.clips)
        box = AxisAlignedBoundingBox(*self.selection[2:4])
        tiles = dict([((x-box.min.x, y-box.min.y),
                       self.tiles.get((x,y),"water"))
                      for (x,y) in box.enumerate()])
        item = ClipboardItem(tiles, box.size, self.map)
        self.clips.append(item)
        self.selectionId = self.edit.selectionId
        self.window.dialogMessage("Copied selection.")
    
    def paste(self):
        tiles = self.getCurTiles()
        if (tiles is None or
            not len(self.selection) == 4):
            return
        box = AxisAlignedBoundingBox(*self.selection[2:4])
        # Draw it into the current selection area
        for row in range(box.min.y, box.max.y + 1):
            for col in range(box.min.x, box.max.x + 1):
                tile = tiles[(col-box.min.x, row-box.min.y)]
                if tile != "water":
                    self.world.setTile(col, row, tile)
                elif (col,row) in self.tiles:
                    del self.tiles[(col, row)]
                    self.world.dirty = True
        self.window.map.dirty = True
        self.window.dialogMessage("Pasted floating selection.")
        
    def put(self):
        """ Put a floating selection layer into the image. """
        item = self.getCurItem()
        if item is None:
            return None
        rcWhere = pygame.Rect((0,0),item.size.pos)
        rcWhere.center = (self.map.getCenterTile() - (item.size / 2)).pos
        
        # Now create a selection.
        self.edit.clearSelection()
        abWhere = AxisAlignedBoundingBox(Point(*rcWhere.topleft),
                                         Point(*rcWhere.bottomright))
        self.edit.selection[:] = [abWhere.min, abWhere.max,
                                  abWhere.min, abWhere.max]
        self.edit.toolBox.setTool("move")
        tool = self.edit.toolBox.getCurTool()
        tool.moveSrc = item
        tool.moveSelectionId = self.edit.selectionId
        self.selectionId = self.edit.selectionId
    
    def getCurTiles(self):
        if self.curClip < 0:
            return None
        return self.clips[self.curClip].tiles

    def getCurItem(self):
        if self.curClip < 0:
            return None
        return self.clips[self.curClip]



    def drawMenu(self):
        EditPalette.drawMenu(self)

        # Draw the current clip list
        rcMenu = self.map.mainWindow.overlayRects["menu"]
        screen = self.map.mainWindow.screen
        width = (self.map.mainWindow.menuW - 40)
        left,top = rcMenu.topleft
        top += 16
        left += 20
        for n in range(-1, -len(self.clips) - 1,-1):
            preview = self.clips[n].getPreview()
            screen.blit(preview, (left + (width - preview.get_width())/2,top))
            top += preview.get_height() + 10
            if top >= rcMenu.height:
                break

        return False


